Current Weather Quicklooks#

Site Overview#

The sites:

  • W099 - Northwestern University (NU)

  • W08D - Northeastern Illinois University (NEIU)

  • W08E - Chicago State University (CSU)

current CROCUS sites

import sage_data_client
start = "-1h"
Hide code cell source
wxt_df = sage_data_client.query(
    start=start,
    filter={
        "sensor": "vaisala-wxt536",
        "name": "wxt.wind.*",
        "vsn": "W08D|W099"
    }
)
---------------------------------------------------------------------------
TimeoutError                              Traceback (most recent call last)
File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/urllib/request.py:1348, in AbstractHTTPHandler.do_open(self, http_class, req, **http_conn_args)
   1347 try:
-> 1348     h.request(req.get_method(), req.selector, req.data, headers,
   1349               encode_chunked=req.has_header('Transfer-encoding'))
   1350 except OSError as err: # timeout error

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/http/client.py:1283, in HTTPConnection.request(self, method, url, body, headers, encode_chunked)
   1282 """Send a complete request to the server."""
-> 1283 self._send_request(method, url, body, headers, encode_chunked)

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/http/client.py:1329, in HTTPConnection._send_request(self, method, url, body, headers, encode_chunked)
   1328     body = _encode(body, 'body')
-> 1329 self.endheaders(body, encode_chunked=encode_chunked)

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/http/client.py:1278, in HTTPConnection.endheaders(self, message_body, encode_chunked)
   1277     raise CannotSendHeader()
-> 1278 self._send_output(message_body, encode_chunked=encode_chunked)

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/http/client.py:1038, in HTTPConnection._send_output(self, message_body, encode_chunked)
   1037 del self._buffer[:]
-> 1038 self.send(msg)
   1040 if message_body is not None:
   1041 
   1042     # create a consistent interface to message_body

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/http/client.py:976, in HTTPConnection.send(self, data)
    975 if self.auto_open:
--> 976     self.connect()
    977 else:

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/http/client.py:1448, in HTTPSConnection.connect(self)
   1446 "Connect to a host on a given (SSL) port."
-> 1448 super().connect()
   1450 if self._tunnel_host:

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/http/client.py:942, in HTTPConnection.connect(self)
    941 sys.audit("http.client.connect", self, self.host, self.port)
--> 942 self.sock = self._create_connection(
    943     (self.host,self.port), self.timeout, self.source_address)
    944 # Might fail in OSs that don't implement TCP_NODELAY

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/socket.py:857, in create_connection(address, timeout, source_address)
    856 try:
--> 857     raise err
    858 finally:
    859     # Break explicitly a reference cycle

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/socket.py:845, in create_connection(address, timeout, source_address)
    844     sock.bind(source_address)
--> 845 sock.connect(sa)
    846 # Break explicitly a reference cycle

TimeoutError: [Errno 110] Connection timed out

During handling of the above exception, another exception occurred:

URLError                                  Traceback (most recent call last)
Cell In[2], line 1
----> 1 wxt_df = sage_data_client.query(
      2     start=start,
      3     filter={
      4         "sensor": "vaisala-wxt536",
      5         "name": "wxt.wind.*",
      6         "vsn": "W08D|W099"
      7     }
      8 )

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/site-packages/sage_data_client/query.py:115, in query(start, end, head, tail, filter, endpoint, bucket, experimental_func, experimental_window)
    112 headers = {"Accept-Encoding": "gzip"}
    113 req = Request(endpoint, data, headers=headers)
--> 115 with urlopen(req) as f:
    116     content_encoding = f.headers.get("Content-Encoding", "")
    117     if "gzip" in content_encoding:

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/urllib/request.py:216, in urlopen(url, data, timeout, cafile, capath, cadefault, context)
    214 else:
    215     opener = _opener
--> 216 return opener.open(url, data, timeout)

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/urllib/request.py:519, in OpenerDirector.open(self, fullurl, data, timeout)
    516     req = meth(req)
    518 sys.audit('urllib.Request', req.full_url, req.data, req.headers, req.get_method())
--> 519 response = self._open(req, data)
    521 # post-process response
    522 meth_name = protocol+"_response"

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/urllib/request.py:536, in OpenerDirector._open(self, req, data)
    533     return result
    535 protocol = req.type
--> 536 result = self._call_chain(self.handle_open, protocol, protocol +
    537                           '_open', req)
    538 if result:
    539     return result

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/urllib/request.py:496, in OpenerDirector._call_chain(self, chain, kind, meth_name, *args)
    494 for handler in handlers:
    495     func = getattr(handler, meth_name)
--> 496     result = func(*args)
    497     if result is not None:
    498         return result

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/urllib/request.py:1391, in HTTPSHandler.https_open(self, req)
   1390 def https_open(self, req):
-> 1391     return self.do_open(http.client.HTTPSConnection, req,
   1392         context=self._context, check_hostname=self._check_hostname)

File ~/miniconda3/envs/instrument-cookbooks-dev/lib/python3.10/urllib/request.py:1351, in AbstractHTTPHandler.do_open(self, http_class, req, **http_conn_args)
   1348         h.request(req.get_method(), req.selector, req.data, headers,
   1349                   encode_chunked=req.has_header('Transfer-encoding'))
   1350     except OSError as err: # timeout error
-> 1351         raise URLError(err)
   1352     r = h.getresponse()
   1353 except:

URLError: <urlopen error [Errno 110] Connection timed out>

Code to Create Plots#

Hide code cell source
import sage_data_client
from bokeh.models.formatters import DatetimeTickFormatter
import hvplot.pandas
import hvplot.xarray
import holoviews as hv
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import xarray as xr
import matplotlib.pyplot as plt
from metpy.plots import USCOUNTIES
import metpy.calc as mpcalc
import act
import numpy as np
import pandas as pd
import warnings
from bokeh.models import DatetimeTickFormatter
import panel as pn

start = "-6h"

def apply_formatter(plot, element):
    plot.handles['xaxis'].formatter = DatetimeTickFormatter(hours='%m/%d/%Y \n %H:%M',
                                                            minutes='%m/%d/%Y \n %H:%M',
                                                            hourmin='%m/%d/%Y \n %H:%M',
                                                            days='%m/%d/%Y \n %H:%M',
                                                            months='%m/%d/%Y \n %H:%M')
xr.set_options(keep_attrs=True)
warnings.filterwarnings("ignore")
hv.extension("bokeh")



# Dictionary for renaming to standard names
variable_rename_dict = {'wxt.env.humidity':'relative_humidity',
                        'wxt.env.pressure':'air_pressure',
                        'wxt.env.temp':'air_temperature',
                        'wxt.heater.temp':'heater_temperature',
                        'wxt.heater.volt':'heater_voltage',
                        'wxt.rain.accumulation':'rain_accumulation',
                        'wxt.wind.direction':'wind_direction',
                        'wxt.wind.speed':'wind_speed',
                        'sys.gps.lat':'latitude',
                        'sys.gps.lon':'longitude',
                    }

# Dictionary for units that are missing
units_dict = {'wxt.env.temp': 'degC',
              'wxt.env.pressure':'hPa',
              'wxt.env.humidity':'percent',
              'wxt.wind.speed':'m/s',
              'wxt.wind.direction':'degrees'}

def generate_data_array(df, variable, rename_variable_dict=variable_rename_dict):
    new_variable_name = rename_variable_dict[variable]
    df_variable= df.loc[df.name == variable]
    ds = df_variable.to_xarray().rename({'value':new_variable_name,
                                         'timestamp':'time',
                                         'meta.vsn':'node'})
    ds[new_variable_name].attrs['units'] = df_variable['meta.units'].values[0]
    ds['time'] = pd.to_datetime(ds.time)
    ds.attrs['datastream'] = ds.node.values[0]
    return ds[[new_variable_name]]

def generate_dataset(df, variables, rename_variable_dict=variable_rename_dict):
    try:
        reindexed = df.set_index(['meta.vsn', 'timestamp'])
    except:
        reindexed = df.set_index(['timestamp'])
    return xr.merge([generate_data_array(reindexed, variable) for variable in variables])

crocus_nodes = "W08D|W099"


# Query and load for n numbder of days
wxt_df = sage_data_client.query(
    start=start,
    filter={
        "sensor": "vaisala-wxt536",
        "name": "wxt.env.*",
        "vsn": crocus_nodes
    }
)

wxt_df1 = sage_data_client.query(
    start=start,
    filter={
        "sensor": "vaisala-wxt536",
        "name": "wxt.wind.*",
        "vsn": crocus_nodes
    }
)

wxt_df = pd.concat([wxt_df, wxt_df1])

try:

    wxt_variables = wxt_df.name.unique()
    wxt_df['meta.units'] = wxt_df.name.map(units_dict)
    wxt_ds = generate_dataset(wxt_df, wxt_variables).squeeze().metpy.parse_cf()
    wxt_ds['air_dewpoint_temperature'] = mpcalc.dewpoint_from_relative_humidity(wxt_ds.air_temperature, wxt_ds.relative_humidity)
    
    # Resample to 1 minute freqency
    minute_ds = wxt_ds.resample(time='1T').mean()
    
    
    plots = []
    for node in minute_ds.node.values:
        minute_ds_subset = minute_ds.sel(node=node)
        wxt_ds_subset = wxt_ds.sel(node=node)
        temp_plot = minute_ds_subset.air_temperature.hvplot(color='red',
                                                     label='Air Temperature (degC)')
        dewp_plot = minute_ds_subset.air_dewpoint_temperature.hvplot(color ='green',
                                                              label='Dewpoint Temperature (degC)')
        plots.append((temp_plot * dewp_plot).opts(hooks=[apply_formatter]))
        
        meteogram_variables = ['wind_speed', 'wind_direction']
        for variable in meteogram_variables:
            plots.append((wxt_ds_subset[variable].hvplot.line(label='10 Hz Data') * 
                         minute_ds[variable].hvplot.line(label='1 Minute Data')).opts(hooks=[apply_formatter]))
    
    
except:
    minute_ds = None
    plots = pn.Row('No Data Available')
Hide code cell source
hv.Layout(plots).cols(1)

Temperature (degC) (Past 24 Hours)#

Temperature in degrees Celsius for the past 24 hours!

Hide code cell source
from IPython.display import IFrame
IFrame('https://portal.sagecontinuum.org/query-browser?apps=registry.sagecontinuum.org%2Fjrobrien%2Fwaggle-wxt536%3A0.23.5.08&nodes=W099%7CW08D&names=wxt.env.temp&start=-1d', width=800, height=400)

Wind Speed (m/s) (Past 24 Hours)#

Wind speed in meters per second for the past 24 hours!

Hide code cell source
from IPython.display import IFrame
IFrame('https://portal.sagecontinuum.org/query-browser?apps=registry.sagecontinuum.org%2Fjrobrien%2Fwaggle-wxt536%3A0.23.5.08&nodes=W099%7CW08D&start=-1d&names=wxt.wind.speed', width=800, height=400)

Wind Direction (degrees) (Past 24 Hours)#

Wind direction in degrees for the past 24 hours!

Hide code cell source
from IPython.display import IFrame
IFrame('https://portal.sagecontinuum.org/query-browser?apps=registry.sagecontinuum.org%2Fjrobrien%2Fwaggle-wxt536%3A0.23.5.08&nodes=W099%7CW08D&start=-1d&names=wxt.wind.direction', width=800, height=400)

Wind Rose Plot#

This plot shows the freqency and direction of the winds, separated by the magnitude of the winds. This helps with identifying where the wind was coming from and how strong that wind was.

Hide code cell source
if minute_ds is not None:
    for node in minute_ds.node.values:
        minute_ds_subset = minute_ds.sel(node=node)
        
        if node == "W08D":
            minute_ds_subset.attrs["datastream"] = "W08D Northeastern Illinois Univ."
        elif node == "W099":
            minute_ds_subset.attrs["datastream"] = "W099 Northwestern Univ."
        WindDisplay = act.plotting.WindRoseDisplay(minute_ds_subset, figsize=(6, 8), subplot_shape=(1,))
        WindDisplay.plot(
            'wind_direction', 'wind_speed', spd_bins=np.linspace(0, 20, 5), num_dirs=30, tick_interval=2, subplot_index=(0,)
        )
        plt.show()
else:
    print('Issue with wxt datastream')
../../_images/e255efb9d12e962f0abf682051dc260389e953465c57e153c4020e8d3e05c377.png ../../_images/2f370d7d1740fc1d0e763f629feacf2d6c5096aea0c1c9795608ae93dfd9cb8c.png